package com.ruoyi.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public final class DTOUtils {

    private static final Logger log = LoggerFactory.getLogger(DTOUtils.class);

    private DTOUtils() {
    }

    /**
     * 复制DTO对象属性值（忽略源对象属性中的空值）
     * @See DTOUtils#copyFields
     */
    @Deprecated
    public static <T, S> T copyIgnoreNull(@NotNull T target, @NotNull S source, String ignoreFields){
        return copyFields(source, target, ignoreFields, true);
    }

    /**
     *  复制DTO对象属性值（不忽略源对象属性中的空值）
     * @param source 源对象
     * @param target 目标对象
     * @return
     */
    public static <S, T> T copyFields(@NotNull S source, @NotNull T target){
        return copyFields(source, target, null, false);
    }

    /**
     * 复制DTO对象属性值（不忽略源对象属性中的空值）
     * @param source 源对象
     * @param target 目标对象
     * @param ignoreFields 指定需要忽略复制的属性字段名（使用逗号分隔）
     * @return
     */
    public static <S, T> T copyFields(@NotNull S source, @NotNull T target, String ignoreFields){
        return copyFields(source, target, ignoreFields, false);
    }

    /**
     *   复制DTO对象属性值（不忽略源对象属性中的空值）
     * @param source  源对象
     * @param target 目标对象
     * @param specifiedFields 指定需要复制的属性字段名（使用逗号分隔）
     * @return
     */
    public static <S, T> T copySpecifiedFields(@NotNull S source, @NotNull T target, String specifiedFields){
        return copyFields(source, target, null, specifiedFields, false);
    }
    /**
     * 复制DTO对象属性值
     * @param target 目标对象
     * @param source 数据对象
     * @param ignoreFields 忽略字段,从逗号分隔多个字段名
     * @param ignoreSourceFieldIsBlank 是否忽略源对象中空值的字段
     * @return
     */
    public static <S, T> T copyFields(@NotNull S source, @NotNull T target, String ignoreFields, boolean ignoreSourceFieldIsBlank){
        return copyFields(source, target, ignoreFields, null, ignoreSourceFieldIsBlank);
    }
    private static <S, T> T copyFields(@NotNull S source, @NotNull T target, String ignoreFields, String specifiedFields, boolean ignoreSourceFieldIsBlank){
        return copyFields(source, target,
            StringUtils.splitAndTrim(ignoreFields, ","),
            StringUtils.splitAndTrim(specifiedFields, ","),
            ignoreSourceFieldIsBlank
            );
    }
    private static <S, T> T copyFields(@NotNull S source, @NotNull T target, String[] ignoreFields, String[] specifiedFields, boolean ignoreSourceFieldIsBlank){
        if (Objects.isNull(target) || Objects.isNull(source)){
            throw new NullPointerException();
        }
        Class sourceClass = source.getClass();
        boolean isSourceMap = (Map.class.isAssignableFrom(sourceClass));

        Class targetClass = target.getClass();
        List<String> ignores = ignoreFields == null?null:Arrays.asList(StringUtils.toLowerCase(ignoreFields));
        List<String> copies = specifiedFields == null?null:Arrays.asList(StringUtils.toLowerCase(specifiedFields));

        for (Method method : sourceClass.getMethods()) {
            if(method.getName().equals("getClass")) continue;
            if(method.getName().startsWith("get") || method.getName().startsWith("is")){
                try {
                    Object value = method.invoke(source);
                    if( ignoreSourceFieldIsBlank && ToolUtil.isEmpty(value) ) continue;
                    String fieldName = method.getName().substring(
                        method.getName().startsWith("get") ? 3 : 2
                    );
                    if ((ignores==null||!ignores.contains(fieldName.toLowerCase()))
                        &&(copies==null||copies.contains(fieldName.toLowerCase()))){
                        targetClass.getMethod("set"+fieldName,value.getClass())
                            .invoke(target,value);
                    }
                } catch (Exception e) {
                    log.debug(e.getMessage());
                }
            }
        }
        return target;
    }

/*
    public static <T> T removeOtherFields(T dto, String... fieldNames) {
        return removeFields(dto, true, fieldNames);
    }
    public static <T> T removeFields(T dto, String... fieldNames) {
        return removeFields(dto, false, fieldNames);
    }
    private static <T> T removeFields(T dto, boolean isRemoveOtherFields, String... fieldNames) {
        if(dto == null) return null;

        List<String> names = Arrays.asList(fieldNames);
        Class cls = dto.getClass();
        Field[] fields = cls.getDeclaredFields();
        //Object[] args = new Object[]{null};
        for (Field field : fields) {
            String name = field.getName();
            boolean doRemove = names.contains(name);
            if (isRemoveOtherFields) doRemove = !doRemove;
            if (doRemove) {
                try {
                    field.setAccessible(true);
                    field.set(dto, null);
                } catch (Exception ex) {
                    log.debug("DTOUtils.removeOtherFields: {}", ex.getMessage());
                }
            }
        }
        return dto;
    }*/


    /**
     * 将对象转换为json，忽略转换时可能出现的异常，如果转换有异常返回null.
     * @param dto
     * @return
     */
    public static String tryToJson(Object dto){
        try {
            return toJson(dto);
        }catch(Exception ex){
            log.debug("json转换失败：{}", ex.getMessage());
        }
        return null;
    }

    private static String toJson(Object dto) throws Exception{
        //return objectMapper.writeValueAsString(dto);
        return null==dto ? null : JsonUtil.toJson(dto);
    }
    /**
     * 将json转换为对象，忽略转换时可能出现的异常，如果转换有异常返回null.
     * @param json
     * @param dtoClass
     * @param <T>
     * @return
     */
    public static <T> T tryFromJson(String json, Class<T> dtoClass){
        try {
            return fromJson(json, dtoClass);
        }catch(Exception ex){
            log.debug("json转换失败：{}", ex.getMessage());
        }
        return null;
    }
    private static <T> T fromJson(String json, Class<T> dtoClass) throws Exception{
        return StringUtils.isBlank(json) ? null : JsonUtil.fromJson(json, dtoClass);
    }

}
